《Android 进阶(三)》 自定义View之支持Gravity的ViewGroup

1. 前言

前面两篇博客主要是介绍直接继承View后复写onDraw方法来实现一些不规则图形的绘制,来达到满足不同自定义View的需求,更注重的是图形的绘制变换和效果展示,前两天学习一些自定义ViewGroup的相关内容,分享一下。

2. 目标

支持Gravity的ViewGroup。

支持的Gravity的种类:左上,右上,左下,右下,中心。

3. 实现步骤

  1. 自定义属性:custom_gravity
  2. 自定义属性的取值范围:1-5(topleft, topright, bottomleft, bottomright, center)
  3. 自定义LayoutParam。参考LinearLayout,RelativeLayout等等
  4. 复写onMeasure, onLayout
  5. 复写LayoutParam相关的几个方法

4. 几点提醒

  1. onMeasure使用来确定子view的大小的,没有那么神秘,就是根据ViewGroup的大小和子View的LayoutParam来确定子View应该有的大小。通过
    MeasureSpec.makeMeasureSpec的方式生成MeasureSpec,通过调用子View的measure方法,把数据传递给子View,方便确定大小。
  2. onLayout方法同来确定子View的位置,传入的参数是当前View Group的上下左上角和右下角的位置,通过子View的属性值和一些其他判断条件,来计算子View应该摆放在哪个位置,然后通过调用子View的layout方法来摆放。
  3. 自定义LayoutParam。需要复写几个方法
    这里写图片描述

5. 代码实现

5.1 自定义属性

1
2
3
4
5
6
7
8
9
10
11
<declare-styleable name="CustomViewGroup">
<attr name="custom_gravity"/>

</declare-styleable>
<attr name="custom_gravity">
<flag name="topleft" value="1"/>
<flag name="topright" value="2"/>
<flag name="bottomleft" value="3"/>
<flag name="bottomright" value="4"/>
<flag name="center" value="5"/>
</attr>

5.2 CustomViewGroup

代码写的比较简单,方便理解,把每个子View的大小全部设置成相同的大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
public class CustomViewGroup extends ViewGroup {

public CustomViewGroup(Context context) {
super(context);
}

public CustomViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}

public CustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);

/**
* 每个子View的大小都设置成相同大小
*/
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
int mode = MeasureSpec.EXACTLY;
int childWidth = widthSize / 4;
int childHeight = heightSize / 4;
child.measure(MeasureSpec.makeMeasureSpec(childWidth, mode),
MeasureSpec.makeMeasureSpec(childHeight, mode));
}
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
int width = child.getMeasuredWidth();
int height = child.getMeasuredHeight();
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
int gravity = lp.gravity;
switch (gravity) {
case LayoutParams.TOPLEFT_GRAVITY:
child.layout(l, t, width, height);
break;
case LayoutParams.TOPRIGHT_GRAVITY:
child.layout(r - width, t, r, height);
break;
case LayoutParams.BOTTOMLEFT_GRAVITY:
child.layout(l, b - height, width, b);
break;
case LayoutParams.BOTTOMRIGHT_GRAVITY:
child.layout(r - width, b - height, r, b);
break;
case LayoutParams.CENTER_GRAVITY:
child.layout((r - width) / 2, (b - height) / 2, (r + width) / 2,
(b + height) / 2);
break;
default:
break;
}
}
}

@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}

@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}

@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1);
}


@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}

public static class LayoutParams extends ViewGroup.LayoutParams {
public static final int UNSPECIFIED_GRAVITY = -1;
public static final int TOPLEFT_GRAVITY = 1;
public static final int TOPRIGHT_GRAVITY = 2;
public static final int BOTTOMLEFT_GRAVITY = 3;
public static final int BOTTOMRIGHT_GRAVITY = 4;
public static final int CENTER_GRAVITY = 5;

public int gravity = UNSPECIFIED_GRAVITY;

public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) {
super(c, attrs);

final TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.CustomViewGroup);
gravity = a.getInt(R.styleable.CustomViewGroup_custom_gravity, UNSPECIFIED_GRAVITY);
a.recycle();
}

public LayoutParams(int width, int height, int gravity) {
super(width, height);
this.gravity = gravity;
}

public LayoutParams(@NonNull ViewGroup.LayoutParams source) {
super(source);
this.gravity = TOPLEFT_GRAVITY;
}

public LayoutParams(@NonNull LayoutParams source) {
super(source);

this.gravity = source.gravity;
}
}

}

6. 效果图

这里写图片描述

7. 源代码

源代码还是上传到Github
CustomViewDemo

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×